package com.example.close_order.service;

import com.example.close_order.entity.Order;
import com.example.close_order.enums.OrderStatus;
import com.example.close_order.repository.OrderRepository;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

@Service
public class OrderCloseService {
    
    private static final Logger logger = LoggerFactory.getLogger(OrderCloseService.class);
    
    private static final List<OrderStatus> CLOSEABLE_STATUSES = 
            Arrays.asList(OrderStatus.PENDING, OrderStatus.PAID);
    
    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private RedissonClient redissonClient;
    
    @Autowired
    private NotificationService notificationService;
    
    @Value("${order.close.retry.max:3}")
    private int maxRetryCount;
    
    /**
     * 关闭单个订单（带分布式锁和幂等性校验）
     */
    @Transactional
    public boolean closeOrder(String orderNo) {
        String lockKey = "order:close:lock:" + orderNo;
        RLock lock = redissonClient.getLock(lockKey);
        
        try {
            // 获取分布式锁，避免并发关闭
            if (lock.tryLock(5, 30, TimeUnit.SECONDS)) {
                try {
                    Optional<Order> orderOpt = orderRepository.findByOrderNo(orderNo);
                    if (orderOpt.isEmpty()) {
                        logger.warn("订单不存在: {}", orderNo);
                        return false;
                    }
                    
                    Order order = orderOpt.get();
                    
                    // 幂等性校验：订单已关闭则直接返回成功
                    if (order.getStatus() == OrderStatus.CLOSED) {
                        logger.info("订单已关闭，无需重复处理: {}", orderNo);
                        return true;
                    }
                    
                    // 状态校验：只有待支付和已支付的订单可以关闭
                    if (!CLOSEABLE_STATUSES.contains(order.getStatus())) {
                        logger.warn("订单状态不允许关闭: {}, 当前状态: {}", orderNo, order.getStatus());
                        return false;
                    }
                    
                    // 时效性校验：检查是否已过期
                    if (!order.canBeClosed()) {
                        logger.warn("订单未到关闭时间: {}", orderNo);
                        return false;
                    }
                    
                    // 使用乐观锁更新，防止并发修改
                    int updated = orderRepository.closeOrderWithVersion(
                            order.getId(), 
                            CLOSEABLE_STATUSES,
                            LocalDateTime.now(),
                            LocalDateTime.now(),
                            order.getVersion()
                    );
                    
                    if (updated > 0) {
                        logger.info("订单关闭成功: {}", orderNo);
                        // 异步发送通知
                        sendCloseNotificationAsync(order);
                        return true;
                    } else {
                        logger.warn("订单关闭失败，可能已被其他进程处理: {}", orderNo);
                        return false;
                    }
                    
                } finally {
                    lock.unlock();
                }
            } else {
                logger.warn("获取分布式锁失败: {}", lockKey);
                return false;
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            logger.error("关闭订单时线程被中断: {}", orderNo, e);
            return false;
        } catch (Exception e) {
            logger.error("关闭订单异常: {}", orderNo, e);
            return false;
        }
    }
    
    /**
     * 异步发送关闭通知
     */
    @Async
    public void sendCloseNotificationAsync(Order order) {
        try {
            notificationService.sendOrderCloseNotification(order);
            logger.info("订单关闭通知发送成功: {}", order.getOrderNo());
        } catch (Exception e) {
            logger.error("发送订单关闭通知失败: {}", order.getOrderNo(), e);
        }
    }
    
    /**
     * 批量关闭过期订单（兜底补偿机制）
     */
    @Transactional
    public int batchCloseExpiredOrders() {
        LocalDateTime now = LocalDateTime.now();
        List<Order> expiredOrders = orderRepository.findExpiredOrders(CLOSEABLE_STATUSES, now);
        
        if (expiredOrders.isEmpty()) {
            logger.info("没有发现过期订单需要关闭");
            return 0;
        }
        
        int successCount = 0;
        int totalCount = expiredOrders.size();
        
        logger.info("开始批量关闭过期订单，总数: {}", totalCount);
        
        for (Order order : expiredOrders) {
            try {
                if (closeOrderWithRetry(order.getOrderNo())) {
                    successCount++;
                }
            } catch (Exception e) {
                logger.error("批量关闭订单时发生异常，订单号: {}", order.getOrderNo(), e);
            }
        }
        
        logger.info("批量关闭订单完成，成功: {}，失败: {}", successCount, totalCount - successCount);
        return successCount;
    }
    
    /**
     * 带重试机制的订单关闭
     */
    private boolean closeOrderWithRetry(String orderNo) {
        for (int retry = 0; retry < maxRetryCount; retry++) {
            try {
                if (closeOrder(orderNo)) {
                    return true;
                }
                
                if (retry < maxRetryCount - 1) {
                    Thread.sleep(1000 * (retry + 1)); // 指数退避
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                logger.error("订单关闭重试时线程被中断: {}", orderNo, e);
                break;
            } catch (Exception e) {
                logger.error("订单关闭重试异常，重试次数: {}，订单号: {}", retry + 1, orderNo, e);
            }
        }
        return false;
    }
}